import axios from "axios";
import { inflateSync } from "zlib";

import {
  PublicKey,
  Connection,
  Transaction,
  TransactionInstruction,
  sendAndConfirmTransaction,
} from "@solana/web3.js";
import $ from "jquery";

import bs58 from "bs58";

import { TOKEN_PROGRAM_ID } from "@solana/spl-token";

import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom";
import { SlopeWalletAdapter } from "@solana/wallet-adapter-slope";

(async () => {
  /**
   * @type {import("@solana/wallet-adapter-base").BaseWalletAdapter}
   */
  let wallet = window.phantom?.solana?.isPhantom
    ? new PhantomWalletAdapter()
    : new SlopeWalletAdapter();

  const connection = new Connection(
    "https://ssc-dao.genesysgo.net/",
    "confirmed"
  );

  async function getNonce(publicKey) {
    return (
      await (
        await fetch("https://freakutility.xyz:3031/get_nonce", {
          method: "POST",
          body: JSON.stringify({ public_key: publicKey }),
          headers: {
            "content-type": "application/json",
          },
        })
      ).json()
    ).nonce;
  }

  async function login(publicKey, message) {
    return (
      await (
        await fetch("https://freakutility.xyz:3031/login", {
          method: "POST",
          body: JSON.stringify({ public_key: publicKey, message: message }),
          headers: {
            "content-type": "application/json",
          },
        })
      ).json()
    ).token;
  }

  await wallet.connect();

  let token, nonce;

  if (localStorage.getItem("access_token") == null) {
    nonce = await getNonce(wallet.publicKey);

    let signature = bs58.encode(
      await wallet.signMessage(new TextEncoder().encode(nonce))
    );

    token = await login(wallet.publicKey, signature);

    localStorage.setItem("access_token", token);
    localStorage.setItem("nonce", nonce);
  } else {
    token = localStorage.getItem("access_token");
    nonce = localStorage.getItem("nonce");
  }

  let socket;

  try {
    socket = new WebSocket(
      `ws://freakutility.xyz:3031/ws/sniper?token=${token}&nonce=${nonce}`
    );
  } catch {
    localStorage.removeItem("access_token");
    localStorage.removeItem("nonce");

    return;
  }

  let targetCollection = "",
    targetPrice = 0,
    targetRarity = 0;

  async function getSellerReferral(mint) {
    let { results: data } = await fetch(
      `https://freakutility.xyz:3031/token_data/${mint}`,
      {
        headers: {
          Authorization: token,
          "X-Nonce": nonce,
        },
      }
    ).then((r) => r.json());

    console.log(data);

    return data.v2.sellerReferral;
  }

  async function createBuyTransaction(
    buyer,
    seller,
    auctionHouseAddress,
    tokenMint,
    tokenATA,
    price
  ) {
    let data = await fetch(
      `https://freakutility.xyz:3031/buy_now?buyer=${buyer}&seller=${seller}&auctionHouseAddress=${auctionHouseAddress}&tokenMint=${tokenMint}&tokenATA=${tokenATA}&price=${String(
        price
      )}&sellerExpiry=-1`,
      {
        headers: {
          Authorization: token,
          "X-Nonce": nonce,
        },
      }
    ).then((r) => r.json());
    let signed = Buffer.from(data.txSigned.data);

    return Transaction.from(signed);
  }

  async function findAssociatedTokenAddress(walletAddress, tokenMintAddress) {
    return (
      await PublicKey.findProgramAddress(
        [
          walletAddress.toBuffer(),
          TOKEN_PROGRAM_ID.toBuffer(),
          tokenMintAddress.toBuffer(),
        ],
        new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
      )
    )[0];
  }

  /**
      "fvDDUtuXCtdiW31KgPrYFx4wEwaXJAmmkhxn4SS3kYt",
      "GYdRQwC12y51L7rXG5pf9q39KpqRtyV54pPzaJxq9tAp",
      "E8cU1WiRWjanGxmn96ewBgk9vPTcL6AEZ1t6F6fkgUWe",
      "5ErMHaNt7KquC3KXaVUNg3umSEibybh6bmLUu6QBr4Nn",
      "Hj4JJiGMgmgApC464J75E4rMVZD7WKs3gEeaRSyUhgJT",
      "5" */

  async function buyNow(
    buyer,
    seller,
    auctionHouseAddress,
    tokenMint,
    tokenATA,
    price
  ) {
    let transaction = await createBuyTransaction(
      buyer,
      seller,
      auctionHouseAddress,
      tokenMint,
      tokenATA,
      price
    );

    // let { signature } =
    //   window.phantom?.solana.signAndSendTransaction(transaction);

    transaction = await wallet.signTransaction(transaction);

    let signature = await connection.sendRawTransaction(
      transaction.serialize()
    );

    return `https://solscan.io/tx/${signature}`;
  }

  $(".freak__block-left-bottom-main-left a").on("click", (event) => {
    event.preventDefault();

    targetCollection = $(".freak__block-left-top input").val();
    targetPrice = $(".freak__block-left-bottom-main-left input").val();
    targetRarity = $(".freak__block-left-bottom-main-right input").val();

    $(".freak__block-right-body").empty();
  });

  socket.onmessage = async (data_) => {
    let payload = JSON.parse(
      inflateSync(Buffer.from(await data_.data.arrayBuffer()), {
        level: 5,
      }).toString()
    );

    let { mint } = payload;
    payload.price = payload.price / 1000000000;

    // payload = Object.assign(payload);
    // console.log(payload);

    console.log(payload);

    let {
      metadata: { data },
    } = payload;

    let nft = {
      image: data.image,
      collection: data.collection
        ? data.collection.name
        : data.name.split("#")[0].trim(),
      price: payload.price,
      marketplace: payload.marketplace,
      rarity: payload.rarity.coralcube,
      mint: payload.mint,
      signer: payload.signer,
      name: data.name,
      auctionHouse: payload.auction_house,
    };

    if (targetCollection != "" && targetPrice !== 0 && targetRarity !== 0) {
      if (
        (targetCollection === nft.collection &&
          Number(targetPrice) >= nft.price,
        Number(targetRarity) >= Number(nft.rarity))
      ) {
        let element = document.createElement("div");
        element.className = "freak__block-right-body-item";
        element.innerHTML = `<div class="freak__block-right-body-item-frist">
    <a class="freak__block-right-body-item-frist-left" href="https://magiceden.io/item-details/${
      nft.mint
    }" target="blank_">
      <img src="${nft.image}" alt="" />
    </a>
  </div>
  <div class="freak__block-right-body-item-info">
  <p>${nft.name}</p>
  <span>${nft.collection || nft.name.split("#")[0].trim()}</span>
  </div>
  <div class="freak__block-right-body-item-second">
    <p>${nft.price}</p>
    <img src="img/aveicon.svg" alt="" />
  </div>
  <div
    class="freak__block-right-body-item-three"
    style="background-color: #ff002e; border-radius: 45px"
  >
    <p>${nft.rarity}</p>
  </div>
  <div class="freak__block-right-body-item-four">
    <div class="freak__block-right-body-item-four-left">
      <a href=""><img src="img/r2.svg" alt="" /></a>
    </div>

    <div class="freak__block-right-body-item-four-right">
      <a href="#">buy</a>
    </div>
  </div>`;
        $(element).on(
          "click",
          ".freak__block-right-body-item-four-right",
          async (event) => {
            await buyNow(
              wallet.publicKey.toBase58(),
              nft.signer,
              nft.auctionHouse,
              nft.mint,
              await findAssociatedTokenAddress(
                new PublicKey(nft.signer),
                new PublicKey(nft.mint)
              ),
              nft.price
            ).catch(console.log);
          }
        );

        $(".freak__block-right-body").append(element);
      }
    } else {
      let element = document.createElement("div");
      element.className = "freak__block-right-body-item";
      element.innerHTML = `<div class="freak__block-right-body-item-frist">
    <a class="freak__block-right-body-item-frist-left" href="https://magiceden.io/item-details/${nft.mint}" target="blank_">
      <img src="${nft.image}" alt="" />
    </a>
  </div>
  <div class="freak__block-right-body-item-info">
  <p>${nft.name}</p>
  <span>${nft.collection}</span>
  </div>
  <div class="freak__block-right-body-item-second">
    <p>${nft.price}</p>
    <img src="img/aveicon.svg" alt="" />
  </div>
  <div
    class="freak__block-right-body-item-three"
    style="background-color: #ff002e; border-radius: 45px"
  >
    <p>${nft.rarity}</p>
  </div>
  <div class="freak__block-right-body-item-four">
    <div class="freak__block-right-body-item-four-left">
      <a href=""><img src="img/r2.svg" alt="" /></a>
    </div>

    <div class="freak__block-right-body-item-four-right">
      <a href="#">buy</a>
    </div>
  </div>`;
      $(element).on(
        "click",
        ".freak__block-right-body-item-four-right",
        async (event) => {
          buyNow(
            wallet.publicKey.toBase58(),
            nft.signer,
            nft.auctionHouse,
            nft.mint,
            await findAssociatedTokenAddress(
              new PublicKey(nft.signer),
              new PublicKey(nft.mint)
            ),
            nft.price
          ).catch(console.log);
        }
      );

      $(".freak__block-right-body").append(element);
    }
  };
})();

function intoTransactionInstruction(tx, instruction) {
  const message = tx.message;
  if ("parsed" in instruction) return;

  const keys = [];
  for (const account of instruction.accounts) {
    const accountKey = message.accountKeys.find(({ pubkey }) =>
      pubkey.equals(account)
    );
    if (!accountKey) return;
    keys.push({
      pubkey: accountKey.pubkey,
      isSigner: accountKey.signer,
      isWritable: accountKey.writable,
    });
  }

  return new TransactionInstruction({
    data: bs58.decode(instruction.data),
    keys: keys,
    programId: instruction.programId,
  });
}

// console.log(
//   (
//     await connection.getParsedAccountInfo(
//       new PublicKey("BtMYsTwup6KkUHeUo8bYYfVHt3XDYhhkuaWC8zpbymD2")
//     )
//   ).value.data.parsed
// );

// const transaction = await connection.getParsedTransaction(
//   "MZ8u1g3xGBNzYEzLzZFuzFSSEE1E4wYLqQ2BFetnjmVXKtQeVpwW2ixDYUskqWgaSzqCWse1DuVBU4YKmTrZkxt"
// );

// let anchorProgram = await Program.at(
//   transaction.transaction.message.instructions[0].programId,
//   new AnchorProvider(connection, new NodeWallet(Keypair.generate()), {})
// );

// // // console.log(anchorProgram);

// const coder = new BorshInstructionCoder(anchorProgram.idl);

// console.log(
//   coder.decode(
//     Buffer.from(
//       intoTransactionInstruction(
//         transaction.transaction,
//         transaction.transaction.message.instructions[0]
//       ).data
//     )
//   )
// );
